package com.casic.ops.runner.pipeline.node;

import com.casic.ops.entity.domain.MachineInfoDO;
import com.casic.ops.entity.dto.pipeline.datastructure.Logger.NodeLogger;
import com.casic.ops.entity.dto.pipeline.datastructure.constant.CommandConst;
import com.casic.ops.entity.dto.pipeline.datastructure.executecontext.PipelineExecuteContext;
import com.casic.ops.handler.pipeline.PipelineExecuteLoggerSocketService;
import com.casic.ops.runner.pipeline.manager.ChannelManager;
import com.casic.ops.runner.ssh.SshClient;
import com.casic.ops.runner.ssh.SshClientFactory;
import com.casic.ops.service.api.pipeline.PipelineNodeLoggerService;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSchException;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;

import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 单独另写一个抽象部署执行节点
 * @author cww
 * @date 2022/6/23
 */
@Slf4j
public abstract class DeployAbstractExecutableNode implements ExecutableNode{

    @Resource
    private PipelineExecuteLoggerSocketService loggerService;
    @Resource
    private PipelineNodeLoggerService pipelineNodeLoggerService;

    public int deployShell(NodeLogger nodeLogger, PipelineExecuteContext executeContext, List<MachineInfoDO> list, String... commands){
        int statusCode=-1;
        long loggerUuid =nodeLogger.getLoggerUuid();
        String nodeUuid = nodeLogger.getNodeUuid();
        //要将loggerUuid转换成String类型，不然无法实时推送日志到前端
        String key = String.format("%s&%s", Long.valueOf(loggerUuid).toString(), nodeUuid);
        List<Integer> statusCodeList=new ArrayList<>();
//        ChannelShell channel=null;
//        InputStream in = null;
//        OutputStream os = null;
        for(MachineInfoDO machineInfoDO:list) {
//            try {
//                channel = channelManager.getChannelShell(machineInfoDO);
//                in = channel.getInputStream();
//                channel.setPty(true);
//                channel.connect();
//                os = channel.getOutputStream();
//                //先说明当前的部署主机
//                String currentMachine=CommandConst.ENTER+"======================"+machineInfoDO.getMachineName()+"主机部署日志"+"======================"+ CommandConst.ENTER;
//                log.info("主机 {} 正在部署",machineInfoDO.getMachineName());
//                nodeLogger.append(currentMachine);
//                //拼接日志
//                loggerService.sendMessage(key,currentMachine);
//                StringBuilder cmdsb = new StringBuilder();
//                for (String cmd : commands) {
//                    cmdsb.append(cmd).append(CommandConst.ENTER);
//                }
//
//                cmdsb.append(CommandConst.EXIT);
//
//                byte[] commandBytes = cmdsb.toString().getBytes();
//                os.write(commandBytes);
//                os.flush();
//
//                //          long startTime = System.currentTimeMillis();
//                byte[] buffer = new byte[1024];
//                while (true) {
//                    while (in.available() > 0) {
//                        int i = in.read(buffer, 0, 1024);
//                        if (i < 0) {
//                            break;
//                        }
//                        //将机器返回的执行信息构建成String类型，
//                        String resp = new String(buffer, 0, i);
//                        if (resp.indexOf(NodeConst.MORE) >= 0) {
//                            os.write((NodeConst.BLANK).getBytes());
//                            os.flush();
//                        }
//                        log.debug(resp);
//                        //                    startTime = System.currentTimeMillis();
//                        log.info("推送日志数据 key:{}", key);
//                        //拼接机器返回的信息,便于保存到数据库
//                        nodeLogger.append(resp);
//                        System.out.println("========================================resp=============================");
//                        System.out.println(resp);
//                        System.out.println("========================================resp==============================");
//                        //通过WebSocket实时返回给前端该节点的执行日志信息
//                        loggerService.sendMessage(key, resp);
//                    }
//                    if (channel.isClosed()) {
//                        if (in.available() > 0) {
//                            continue;
//                        }
//                        int currentstatusCode = channel.getExitStatus();
//                        statusCodeList.add(currentstatusCode);
//                        log.info("主机 "+machineInfoDO.getMachineName()+" exit-status: " + channel.getExitStatus());
//                        break;
//                    }
//                    //十秒钟未响应退出
//                    //                if (System.currentTimeMillis() - startTime > 10000) {
//                    //                    loggerService.sendMessage(key, String.format("%s%s%s", CommandConst.ENTER,"响应超时", CommandConst.ENTER));
//                    //                    break;
//                    //                }
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//            }finally {
//                //关闭IO
//                try{
//                os.close();
//                in.close();
//                }catch (Exception e){
//                    e.printStackTrace();
//                }
//            }
            try {
                SshClient ssh = SshClientFactory.createSsh(machineInfoDO);
                List<String> commandList= Arrays.asList(commands);
                //说明当前部署主机
                String currentMachine=CommandConst.ENTER+"======================"+machineInfoDO.getMachineName()+"主机部署日志"+"======================"+ CommandConst.ENTER;
                log.info("主机 {} 正在部署",machineInfoDO.getMachineName());
                nodeLogger.append(currentMachine);
                loggerService.sendMessage(key,currentMachine);
                //执行命令，并且把命令的执行回传到前端
                int returnCode=ssh.exec(commandList,var->{
                    loggerService.sendMessage(key, var);
                    nodeLogger.append(var);
                });
                //添加当前部署主机的执行状态码
                statusCodeList.add(returnCode);
                log.info("exit-status: " + returnCode);
            }catch (Exception e){
                String errorMessage="与机器建立SSH连接出错";
                ErrorHandle(e,nodeLogger,errorMessage);
            }
        }


        //所有主机部署完成后的统一处理
        //查看主机部署执行返回状态不为0的个数
        long count=statusCodeList.stream().filter(v->v.intValue()!=0).count();
        if(count>0){
            //存在至少一个主机部署失败
            nodeLogger.setFinalStatus("失败");
            statusCode=-1;
        }else{
            //所有主机都部署成功
            nodeLogger.setFinalStatus("成功");
            statusCode=0;
        }
        nodeLogger.serializable();//序列化
        nodeLogger.setExecuteEndTime(new Date());
        pipelineNodeLoggerService.updateByNodeLogger(nodeLogger);
        //主动释放当前socket连接
        loggerService.close(key);
        return statusCode;
    }

    public void ErrorHandle(Exception e,NodeLogger nodeLogger,String errorMessage) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        nodeLogger.append(errorMessage+sw.toString());
        nodeLogger.serializable();//序列化
        nodeLogger.setExecuteEndTime(new Date());
        pipelineNodeLoggerService.updateByNodeLogger(nodeLogger);
        try {
            sw.close();
            pw.close();
        }catch (IOException e2){
            e2.printStackTrace();
        }

    }

}
